package com.test.workdemo.service.impl;

import com.test.workdemo.dao.CeshiTableMapper;
import com.test.workdemo.pojo.entity.CeshiTable;
import com.test.workdemo.service.AsyncDemoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

@Service
@Slf4j
public class AsyncDemoServiceImpl implements AsyncDemoService {

    @Autowired
    private CeshiTableMapper ceshiTableMapper;

    @Override
    public void completableFutureMethod() {
        //supplyAsync是创建带有返回值的异步任务
        CompletableFuture<String> res1 = CompletableFuture.supplyAsync(() -> {
            CeshiTable ceshiTable = ceshiTableMapper.selectById(10);
            String name1 = ceshiTable.getName();
            try {
                Thread.sleep(2000L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("带返回值的执行任务");
            return name1;
        });
        //runAsync是不带返回值的异步任务
        CompletableFuture<Void> res2 = CompletableFuture.runAsync(() -> {
            CeshiTable ceshiTable = new CeshiTable();
            ceshiTable.setAge(9L);
            ceshiTable.setName("ce22");
            ceshiTableMapper.insert(ceshiTable);
            log.info("不带返回值的异步任务,id:{}", ceshiTable.getId());
        });
        //thenApply 表示某个任务执行完成后执行的动作，即回调方法，会将该任务的执行结果即方法返回值作为入参传递到回调方法中，带有返回值,
        //thenApply和thenApplyAsync区别在于，使用thenApply方法时子任务与父任务使用的是同一个线程，而thenApplyAsync在子任务中是另起一个线程执行任务
        CompletableFuture<String> backAsync = res1.thenApply((res) -> {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("回调数据是：");
            stringBuilder.append(res);
            log.info("执行有返回值的回调异步任务，调用数据是：{}", res);
            return stringBuilder.toString();
        });
        //thenAccep表示某个任务执行完成后执行的动作，即回调方法，会将该任务的执行结果即方法返回值作为入参传递到回调方法中，无返回值
        CompletableFuture<Void> noBackAsync = res1.thenAccept((res) -> {
            String noStr = res + ":nn";
            log.info("执行无返回值的参数回调：{}", noStr);
        });
        //thenRun表示某个任务执行完成后执行的动作，即回调方法，无入参，无返回值,使用thenRun方法时子任务与父任务使用的是同一个线程，而thenRunAsync在子任务中可能是另起一个线程执行任务
        CompletableFuture<Void> voidCompletableFuture = res2.thenRun(() -> {
            log.info("执行无参数无返回值的回调");
        });

        //whenComplete是当某个任务执行完成后执行的回调方法，会将执行结果或者执行期间抛出的异常传递给回调方法，如果是正常执行则异常为null, handle跟whenComplete基本一致，
        // 区别在于handle的回调方法有返回值
        CompletableFuture<String> whenCompletableFuture = res1.whenComplete((res, e) -> {
            log.info("执行无返回值的异常回调，数据是{}", res);
            log.info("执行无返回值的异常回调，异常是{}", e);
        });

        CompletableFuture<Throwable> handle = res1.handle((res, e) -> {
            log.info("执行有返回值的异常回调，数据是{}", res);
            log.info("执行有返回值的异常回调，异常是{}", e);
            return e;
        });


        try {
            String getDate = res1.get();
            String backDate = backAsync.get();
            log.info("获得返回值：{}", getDate);
            log.info("获得回调返回值：{}", backDate);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void moreCompletableFutureMethod() throws ExecutionException, InterruptedException {
        CompletableFuture<Long> result1 = CompletableFuture.supplyAsync(() -> {
            CeshiTable ceshiTable = ceshiTableMapper.selectById(11L);
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            Long age = ceshiTable.getAge();
            log.info("执行有返回值的异步任务1，数据是{}", age);
            return age;
        });
        CompletableFuture<Long> result2 = CompletableFuture.supplyAsync(() -> {
            CeshiTable ceshiTable = ceshiTableMapper.selectById(10L);
            Long age = ceshiTable.getAge();
            log.info("执行有返回值的异步任务2，数据是{}", age);
            return age;
        });
        //thenCombine会将两个任务的执行结果作为所提供函数的参数，且该方法有返回值；
        //runAfterBoth没有入参，也没有返回值。注意两个任务中只要有一个执行异常，则将该异常信息作为指定任务的执行结果
        CompletableFuture<Long> all = result1.thenCombine(result2, (res1, res2) -> {
            log.info("异步任务完成后，执行有返回值的多任务联合执行，入参1是{},入参2是{}", res1, res2);
            long allResult = res1 + res2;
            log.info("多任务联合执行结果是:{}",allResult);
            return allResult;
        });
        //thenAcceptBoth同样将两个任务的执行结果作为方法入参，但是无返回值；
        CompletableFuture<Void> noAll = result1.thenAcceptBoth(result2, (res1, res2) -> {
            log.info("异步任务完成后，执行无返回值的多任务联合执行，入参1是{},入参2是{}", res1, res2);
        });
        //allOf：CompletableFuture是多个任务都执行完成后才会执行，只有有一个任务执行异常，则返回的CompletableFuture执行get方法时会抛出异常，如果都是正常执行，则get返回null。
        //anyOf ：CompletableFuture是多个任务只要有一个任务执行完成，则返回的CompletableFuture执行get方法时会抛出异常，如果都是正常执行，则get返回执行完成任务的结果
        CompletableFuture<Void> alledOf = CompletableFuture.allOf(result1, result2, all);
        log.info("获得多个任务的执行结果：{}",alledOf.get());
        Long getResult = all.get();
        log.info("获取多任务处理结果数据：{}",getResult);
    }
}
